/*
 * IdentityFilter.java
 *
 * Created on September 7, 2007, 3:44 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.www.util;

import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import org.infoset.xml.Name;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.data.Cookie;
import org.restlet.data.Reference;
import org.restlet.routing.Filter;

/**
 *
 * @author alex
 */
public class IdentityFilter extends Filter
{
   static int MAX_CACHE = 1000;
   static long MAX_AGE = 5*60*1000;
   
   TreeMap<String,Long> createdTimes;
   TreeMap<String,Identity> identities;
   
   public static Name NAME = Name.create("{http://www.atomojo.org/Vocabulary/Auth/2007/1/0}name");
   public static Name EMAIL = Name.create("{http://www.atomojo.org/Vocabulary/Auth/2007/1/0}email");
   public static Name GROUP = Name.create("{http://www.atomojo.org/Vocabulary/Auth/2007/1/0}group");
   public static Name ROLE = Name.create("{http://www.atomojo.org/Vocabulary/Auth/2007/1/0}role");

   static Reference getReferenceAttribute(Request request,String name,Reference defaultValue)
   {
      Object o = request.getAttributes().get(name);
      return o==null ? defaultValue : (Reference)o;
   }

   boolean lookup;
   long maxAge;
   int maxCache;
   String cookieName;
   Reference confService;
   /** Creates a new instance of IdentityFilter */
   public IdentityFilter(Context context)
   {
      this(context,null,null);
   }
   public IdentityFilter(Context context,Restlet next,Reference service)
   {
      super(context,next);
      this.confService = service;
      lookup =  "true".equals(context.getParameters().getFirstValue("identity.lookup"));
      createdTimes = new TreeMap<String,Long>();
      identities = new TreeMap<String,Identity>();
      String maxAgeS = context.getParameters().getFirstValue("identity.max.age");
      maxAge = maxAgeS==null ? MAX_AGE : Long.parseLong(maxAgeS)*1000;
      String maxCacheS = context.getParameters().getFirstValue("identity.max.cache");
      maxCache = maxCacheS==null ? MAX_CACHE : Integer.parseInt(maxCacheS);
      cookieName = context.getParameters().getFirstValue("identity.cookie");
      if (cookieName==null) {
         cookieName = "I";
      }
      getLogger().info("identity.max.age="+(maxAge/1000)+"s, identity.max.cache="+maxCache+", identity.lookup="+lookup);

      IdentityManager manager = (IdentityManager)context.getAttributes().get(IdentityManager.ATTR);
      if (manager==null) {
         manager = new IdentityManager() {
            public void add(String id,Identity identity) {
               IdentityFilter.this.addIdentity(id, identity);
            }
            
            public boolean remove(String id) {
               return IdentityFilter.this.removeIdentity(id);
            }
         };
         context.getAttributes().put(IdentityManager.ATTR, manager);
      }
      getLogger().info("Identity Manager: "+manager);
   }
   
   public boolean removeIdentity(String id) {
      createdTimes.remove(id);
      return identities.remove(id)!=null;
   }
   
   public void addIdentity(String id,Identity identity)
   {
      identities.put(id,identity);
      createdTimes.put(id,new Long(System.currentTimeMillis()));
   }
   
   protected int beforeHandle(Request request,Response response)
   {
      Reference service = getReferenceAttribute(request,"auth-service",confService);
      Cookie cookie = request.getCookies().getFirst(cookieName);
      if (cookie!=null) {
         getLogger().info("cookie: "+cookieName+"="+cookie.getValue());
         Identity identity = identities.get(cookie.getValue());
         if (identity!=null) {
            getLogger().info("Found "+identity.getAlias()+", checking age...");
            // check age
            Long created = createdTimes.get(cookie.getValue());
            if (created==null) {
               getLogger().info("Expiring.");
               identities.remove(cookie.getValue());
               identity = null;
            } else if ((System.currentTimeMillis()-created.longValue())>=maxAge) {
               removeIdentity(cookie.getValue());
               identity = null;
            }
         }
         if (identity==null) {
            // TODO: make this configurable
            if (!lookup) {
               return Filter.CONTINUE;
            }
            getLogger().info("Looking up identity "+cookie.getValue());
            try {
               identity = RealmGuard.checkSession(getContext().createChildContext(),service, cookie.getValue());
            } catch (RequestFailedException ex) {
               if (ex.getStatus()!=null) {
                  getLogger().severe("Cannot check session against service, status="+ex.getStatus().getCode());
               } else {
                  getLogger().log(Level.SEVERE,"Cannot authenticate against service.",ex);
               }
            }
            if (identity!=null) {
               addIdentity(cookie.getValue(),identity);
            }
         }
         if (identity!=null) {
            addIdentity(request,identity);
         }
      } else {
         getLogger().fine("No I cookie.");
      }
      return Filter.CONTINUE;
   }
   
   public static void addIdentity(Request request,Identity identity) {
      Map<String,Object> atts = request.getAttributes();
      atts.put(Identity.IDENTITY_ATTR,identity);
      atts.put(Identity.PARAMETER_IDENTITY_ID, identity.getId());
      if (identity.getAlias()!=null) {
         atts.put(Identity.PARAMETER_IDENTITY_ALIAS, identity.getAlias());
      }
      if (identity.getName()!=null) {
         atts.put(Identity.PARAMETER_IDENTITY_NAME, identity.getName());
      }
      if (identity.getEmail()!=null) {
         atts.put(Identity.PARAMETER_IDENTITY_EMAIL, identity.getEmail());
      }
      atts.put(Identity.PARAMETER_IDENTITY_SESSION, identity.getSession());
   }
   
}
